dhcpv4: create struct dhcpv4_lease
authorDavid Härdeman <[email protected]>
Thu, 9 Oct 2025 05:13:58 +0000 (07:13 +0200)
committerÁlvaro Fernández Rojas <[email protected]>
Tue, 4 Nov 2025 07:51:16 +0000 (08:51 +0100)
This makes it easier to understand what is actually going on, makes the
structs clearer and avoids layers of indirection like using free
callbacks. I understand the original motivation for unifying the struct
for DHCPv4 and DHCPv6, but it's more hassle than it is worth.

Signed-off-by: David Härdeman <[email protected]>
Link: https://github.com/openwrt/odhcpd/pull/292
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/config.c
src/dhcpv4.c
src/dhcpv6-ia.c
src/odhcpd.h
src/ubus.c

index 113357894f21ac418d233770d118d19cd5dcea5e..c424266c6b4a396e15bcef4e993f5f483832d866 100644 (file)
@@ -1108,7 +1108,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
                iface->ndp_ping_fd = -1;
                iface->dhcpv4_event.uloop.fd = -1;
                INIT_LIST_HEAD(&iface->ia_assignments);
-               INIT_LIST_HEAD(&iface->dhcpv4_assignments);
+               INIT_LIST_HEAD(&iface->dhcpv4_leases);
                INIT_LIST_HEAD(&iface->dhcpv4_fr_ips);
 
                set_interface_defaults(iface);
@@ -1744,24 +1744,29 @@ static int set_interface(struct uci_section *s)
        return config_parse_interface(blob_data(b.head), blob_len(b.head), s->e.name, true);
 }
 
-static void lease_cfg_delete_assignments(struct lease_cfg *lease_cfg, bool v6)
+static void lease_cfg_delete_dhcpv6_assignments(struct lease_cfg *lease_cfg)
 {
        struct dhcp_assignment *a, *tmp;
-       unsigned int flag = v6 ? OAF_DHCPV6 : OAF_DHCPV4;
 
-       list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list) {
-               if (a->flags & flag)
-                       free_assignment(a);
-       }
+       list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list)
+               free_assignment(a);
 }
 
 static void lease_cfg_update_assignments(struct lease_cfg *lease_cfg)
 {
+       struct dhcpv4_lease *a4 = lease_cfg->dhcpv4_lease;
        struct dhcp_assignment *a;
 
+       if (a4) {
+               free(a4->hostname);
+               a4->hostname = NULL;
+
+               if (lease_cfg->hostname)
+                       a4->hostname = strdup(lease_cfg->hostname);
+       }
+
        list_for_each_entry(a, &lease_cfg->assignments, lease_cfg_list) {
-               if (a->hostname)
-                       free(a->hostname);
+               free(a->hostname);
                a->hostname = NULL;
 
                if (lease_cfg->hostname)
@@ -1827,12 +1832,12 @@ static void lease_cfg_change(struct lease_cfg *lease_cfg_old, struct lease_cfg *
 
        if (lease_cfg_old->ipaddr != lease_cfg_new->ipaddr) {
                lease_cfg_old->ipaddr = lease_cfg_new->ipaddr;
-               lease_cfg_delete_assignments(lease_cfg_old, false);
+               dhcpv4_free_lease(lease_cfg_old->dhcpv4_lease);
        }
 
        if (lease_cfg_old->hostid != lease_cfg_new->hostid) {
                lease_cfg_old->hostid = lease_cfg_new->hostid;
-               lease_cfg_delete_assignments(lease_cfg_old, true);
+               lease_cfg_delete_dhcpv6_assignments(lease_cfg_old);
        }
 
        if (update)
@@ -1843,11 +1848,8 @@ static void lease_cfg_change(struct lease_cfg *lease_cfg_old, struct lease_cfg *
 
 static void lease_cfg_delete(struct lease_cfg *lease_cfg)
 {
-       struct dhcp_assignment *a, *tmp;
-
-       list_for_each_entry_safe(a, tmp, &lease_cfg->assignments, lease_cfg_list)
-               free_assignment(a);
-
+       dhcpv4_free_lease(lease_cfg->dhcpv4_lease);
+       lease_cfg_delete_dhcpv6_assignments(lease_cfg);
        free_lease_cfg(lease_cfg);
 }
 
index 2b0e3a5fe05149cd39f14009c257b0c830030305..aaf6821946f354aae6760e9aadde9854c9e52ee0 100644 (file)
@@ -79,15 +79,15 @@ static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr)
 }
 
 static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *addr,
-                               uint32_t mask)
+                             uint32_t mask)
 {
-       struct dhcp_assignment *a = NULL;
+       struct dhcpv4_lease *lease = NULL;
        struct odhcpd_ref_ip *fr_ip = NULL;
 
-       list_for_each_entry(a, &iface->dhcpv4_assignments, head) {
-               if ((a->accept_fr_nonce || iface->dhcpv4_forcereconf) &&
-                   !a->fr_ip &&
-                   ((a->addr & mask) == (addr->addr.in.s_addr & mask))) {
+       list_for_each_entry(lease, &iface->dhcpv4_leases, head) {
+               if ((lease->accept_fr_nonce || iface->dhcpv4_forcereconf) &&
+                   !lease->fr_ip &&
+                   ((lease->addr & mask) == (addr->addr.in.s_addr & mask))) {
                        if (!fr_ip) {
                                fr_ip = calloc(1, sizeof(*fr_ip));
                                if (!fr_ip)
@@ -96,7 +96,7 @@ static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *add
                                list_add(&fr_ip->head, &iface->dhcpv4_fr_ips);
                                fr_ip->addr = *addr;
                        }
-                       inc_ref_cnt_ip(&a->fr_ip, fr_ip);
+                       inc_ref_cnt_ip(&lease->fr_ip, fr_ip);
                }
        }
 
@@ -176,7 +176,7 @@ enum {
        IOV_FR_TOTAL
 };
 
-static void dhcpv4_fr_send(struct dhcp_assignment *a)
+static void dhcpv4_fr_send(struct dhcpv4_lease *lease)
 {
        struct dhcpv4_message fr = {
                .op = DHCPV4_OP_BOOTREPLY,
@@ -214,7 +214,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
        struct dhcpv4_option_u32 fr_serverid = {
                .code = DHCPV4_OPT_SERVERID,
                .len = sizeof(struct in_addr),
-               .data = a->fr_ip->addr.addr.in.s_addr,
+               .data = lease->fr_ip->addr.addr.in.s_addr,
        };
        uint8_t fr_end = DHCPV4_OPT_END;
 
@@ -231,13 +231,13 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
        struct sockaddr_in dest = {
                .sin_family = AF_INET,
                .sin_port = htons(DHCPV4_CLIENT_PORT),
-               .sin_addr = { a->addr },
+               .sin_addr = { lease->addr },
        };
 
        odhcpd_urandom(&fr.xid, sizeof(fr.xid));
-       memcpy(fr.chaddr, a->hwaddr, fr.hlen);
+       memcpy(fr.chaddr, lease->hwaddr, fr.hlen);
 
-       if (a->accept_fr_nonce) {
+       if (lease->accept_fr_nonce) {
                uint8_t secretbytes[64] = { 0 };
                md5_ctx_t md5;
 
@@ -247,7 +247,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
                iov[IOV_FR_AUTH_BODY].iov_len = sizeof(fr_auth_body);
                dhcpv4_add_padding(iov, ARRAY_SIZE(iov));
 
-               memcpy(secretbytes, a->key, sizeof(a->key));
+               memcpy(secretbytes, lease->key, sizeof(lease->key));
                for (size_t i = 0; i < sizeof(secretbytes); ++i)
                        secretbytes[i] ^= 0x36;
 
@@ -272,54 +272,54 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
        }
 
        if (dhcpv4_send_reply(iov, ARRAY_SIZE(iov), (struct sockaddr *)&dest, sizeof(dest),
-                             &a->iface->dhcpv4_event.uloop.fd) < 0)
+                             &lease->iface->dhcpv4_event.uloop.fd) < 0)
                error("Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(fr_msg.data),
-                     odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+                     odhcpd_print_mac(lease->hwaddr, sizeof(lease->hwaddr)), inet_ntoa(dest.sin_addr));
        else
                debug("Sent %s to %s - %s", dhcpv4_msg_to_string(fr_msg.data),
-                     odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+                     odhcpd_print_mac(lease->hwaddr, sizeof(lease->hwaddr)), inet_ntoa(dest.sin_addr));
 }
 
-static void dhcpv4_fr_stop(struct dhcp_assignment *a)
+static void dhcpv4_fr_stop(struct dhcpv4_lease *lease)
 {
-       uloop_timeout_cancel(&a->fr_timer);
-       decr_ref_cnt_ip(&a->fr_ip, a->iface);
-       a->fr_cnt = 0;
-       a->fr_timer.cb = NULL;
+       uloop_timeout_cancel(&lease->fr_timer);
+       decr_ref_cnt_ip(&lease->fr_ip, lease->iface);
+       lease->fr_cnt = 0;
+       lease->fr_timer.cb = NULL;
 }
 
 static void dhcpv4_fr_timer(struct uloop_timeout *event)
 {
-       struct dhcp_assignment *a = container_of(event, struct dhcp_assignment, fr_timer);
+       struct dhcpv4_lease *lease = container_of(event, struct dhcpv4_lease, fr_timer);
 
-       if (a->fr_cnt > 0 && a->fr_cnt < 8) {
-               dhcpv4_fr_send(a);
-               uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
-               a->fr_cnt++;
+       if (lease->fr_cnt > 0 && lease->fr_cnt < 8) {
+               dhcpv4_fr_send(lease);
+               uloop_timeout_set(&lease->fr_timer, 1000 << lease->fr_cnt);
+               lease->fr_cnt++;
        } else
-               dhcpv4_fr_stop(a);
+               dhcpv4_fr_stop(lease);
 }
 
-static void dhcpv4_fr_start(struct dhcp_assignment *a)
+static void dhcpv4_fr_start(struct dhcpv4_lease *lease)
 {
-       uloop_timeout_set(&a->fr_timer, 1000 << a->fr_cnt);
-       a->fr_timer.cb = dhcpv4_fr_timer;
-       a->fr_cnt++;
+       uloop_timeout_set(&lease->fr_timer, 1000 << lease->fr_cnt);
+       lease->fr_timer.cb = dhcpv4_fr_timer;
+       lease->fr_cnt++;
 
-       dhcpv4_fr_send(a);
+       dhcpv4_fr_send(lease);
 }
 
-static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a);
+static void dhcpv4_fr_rand_delay(struct dhcpv4_lease *lease);
 
 static void dhcpv4_fr_delay_timer(struct uloop_timeout *event)
 {
-       struct dhcp_assignment *a = container_of(event, struct dhcp_assignment, fr_timer);
-       struct interface *iface = a->iface;
+       struct dhcpv4_lease *lease = container_of(event, struct dhcpv4_lease, fr_timer);
+       struct interface *iface = lease->iface;
 
-       (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(a) : dhcpv4_fr_start(a));
+       (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(lease) : dhcpv4_fr_start(lease));
 }
 
-static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a)
+static void dhcpv4_fr_rand_delay(struct dhcpv4_lease *lease)
 {
        int msecs;
 
@@ -327,21 +327,49 @@ static void dhcpv4_fr_rand_delay(struct dhcp_assignment *a)
 
        msecs = abs(msecs) % DHCPV4_FR_MAX_FUZZ + DHCPV4_FR_MIN_DELAY;
 
-       uloop_timeout_set(&a->fr_timer, msecs);
-       a->fr_timer.cb = dhcpv4_fr_delay_timer;
+       uloop_timeout_set(&lease->fr_timer, msecs);
+       lease->fr_timer.cb = dhcpv4_fr_delay_timer;
 }
 
-static void dhcpv4_free_assignment(struct dhcp_assignment *a)
+void dhcpv4_free_lease(struct dhcpv4_lease *lease)
 {
-       if (a->fr_ip)
-               dhcpv4_fr_stop(a);
+       if (lease->fr_ip)
+               dhcpv4_fr_stop(lease);
+
+       list_del(&lease->head);
+       if (lease->lease_cfg)
+               lease->lease_cfg->dhcpv4_lease = NULL;
+
+       free(lease->hostname);
+       free(lease);
+}
+
+static struct dhcpv4_lease *
+dhcpv4_alloc_lease(struct interface *iface, const uint8_t *hwaddr, size_t hwaddr_len)
+{
+       struct dhcpv4_lease *lease;
+
+       if (!iface || !hwaddr || hwaddr_len == 0 || hwaddr_len > sizeof(lease->hwaddr))
+               return NULL;
+
+       lease = calloc(1, sizeof(*lease));
+       if (!lease)
+               return NULL;
+
+       INIT_LIST_HEAD(&lease->head);
+
+       lease->hwaddr_len = hwaddr_len;
+       memcpy(lease->hwaddr, hwaddr, hwaddr_len);
+       lease->iface = iface;
+
+       return lease;
 }
 
-static bool dhcpv4_insert_assignment(struct list_head *list, struct dhcp_assignment *a,
-                                    uint32_t addr)
+static bool dhcpv4_insert_lease(struct list_head *list, struct dhcpv4_lease *lease,
+                               uint32_t addr)
 {
        uint32_t h_addr = ntohl(addr);
-       struct dhcp_assignment *c;
+       struct dhcpv4_lease *c;
 
        list_for_each_entry(c, list, head) {
                uint32_t c_addr = ntohl(c->addr);
@@ -354,13 +382,13 @@ static bool dhcpv4_insert_assignment(struct list_head *list, struct dhcp_assignm
        }
 
        /* Insert new node before c (might match list head) */
-       a->addr = addr;
-       list_add_tail(&a->head, &c->head);
+       lease->addr = addr;
+       list_add_tail(&lease->head, &c->head);
 
        return true;
 }
 
-static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
+static bool dhcpv4_assign(struct interface *iface, struct dhcpv4_lease *lease,
                          uint32_t raddr)
 {
        uint32_t start = ntohl(iface->dhcpv4_start_ip.s_addr);
@@ -370,15 +398,15 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
        char ipv4_str[INET_ADDRSTRLEN];
 
        /* Preconfigured IP address by static lease */
-       if (a->addr) {
-               if (!dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, a->addr)) {
+       if (lease->addr) {
+               if (!dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, lease->addr)) {
                        error("The static IP address is already assigned: %s",
-                             inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+                             inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
                        return false;
                }
 
                debug("Assigned static IP address: %s",
-                     inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+                     inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
                return true;
        }
 
@@ -389,19 +417,19 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
        } else if (config_find_lease_cfg_by_ipaddr(raddr)) {
                debug("The requested IP address is statically assigned: %s",
                      inet_ntop(AF_INET, &raddr, ipv4_str, sizeof(ipv4_str)));
-       } else if (!dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, raddr)) {
+       } else if (!dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, raddr)) {
                debug("The requested IP address is already assigned: %s",
                      inet_ntop(AF_INET, &raddr, ipv4_str, sizeof(ipv4_str)));
        } else {
                debug("Assigned the requested IP address: %s",
-                     inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)));
+                     inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)));
                return true;
        }
 
        /* Ok, we'll have to pick an address for the client... */
-       for (size_t i = 0; i < sizeof(a->hwaddr); ++i) {
+       for (size_t i = 0; i < sizeof(lease->hwaddr); ++i) {
                /* ...hash the hwaddr (Knuth's multiplicative method)... */
-               uint8_t o = a->hwaddr[i];
+               uint8_t o = lease->hwaddr[i];
                seed += (o * 2654435761) % UINT32_MAX;
        }
 
@@ -416,9 +444,9 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
                if (config_find_lease_cfg_by_ipaddr(n_try))
                        continue;
 
-               if (dhcpv4_insert_assignment(&iface->dhcpv4_assignments, a, n_try)) {
+               if (dhcpv4_insert_lease(&iface->dhcpv4_leases, lease, n_try)) {
                        debug("Assigned IP adress from pool: %s (succeeded on attempt %u of %u)",
-                             inet_ntop(AF_INET, &a->addr, ipv4_str, sizeof(ipv4_str)),
+                             inet_ntop(AF_INET, &lease->addr, ipv4_str, sizeof(ipv4_str)),
                              i + 1, count);
                        return true;
                }
@@ -429,24 +457,24 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
        return false;
 }
 
-static struct dhcp_assignment *find_assignment_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
+static struct dhcpv4_lease *find_lease_by_hwaddr(struct interface *iface, const uint8_t *hwaddr)
 {
-       struct dhcp_assignment *a;
+       struct dhcpv4_lease *lease;
 
-       list_for_each_entry(a, &iface->dhcpv4_assignments, head)
-               if (!memcmp(a->hwaddr, hwaddr, 6))
-                       return a;
+       list_for_each_entry(lease, &iface->dhcpv4_leases, head)
+               if (!memcmp(lease->hwaddr, hwaddr, 6))
+                       return lease;
 
        return NULL;
 }
 
-static struct dhcp_assignment *
+static struct dhcpv4_lease *
 dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *req_mac,
             const uint32_t req_addr, uint32_t *req_leasetime, const char *req_hostname,
             const size_t req_hostname_len, const bool req_accept_fr, bool *reply_incl_fr,
             uint32_t *fr_serverid)
 {
-       struct dhcp_assignment *a = find_assignment_by_hwaddr(iface, req_mac);
+       struct dhcpv4_lease *lease = find_lease_by_hwaddr(iface, req_mac);
        struct lease_cfg *lease_cfg = config_find_lease_cfg_by_mac(req_mac);
        time_t now = odhcpd_time();
 
@@ -455,109 +483,99 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
         * hwaddr, we need to clear out any old assignments given to other
         * hwaddrs in order to take over the IP address.
         */
-       if (lease_cfg && !a && (req_msg == DHCPV4_MSG_DISCOVER || req_msg == DHCPV4_MSG_REQUEST)) {
-               struct dhcp_assignment *c, *tmp;
-
-               list_for_each_entry_safe(c, tmp, &lease_cfg->assignments, lease_cfg_list) {
-                       if (c->flags & OAF_DHCPV4 && c->flags & OAF_STATIC)
-                               free_assignment(c);
-               }
+       if (lease_cfg && !lease && (req_msg == DHCPV4_MSG_DISCOVER || req_msg == DHCPV4_MSG_REQUEST)) {
+               if (lease_cfg->dhcpv4_lease)
+                       dhcpv4_free_lease(lease_cfg->dhcpv4_lease);
        }
 
-       if (lease_cfg && a && a->lease_cfg != lease_cfg) {
-               free_assignment(a);
-               a = NULL;
+       if (lease_cfg && lease && lease->lease_cfg != lease_cfg) {
+               dhcpv4_free_lease(lease);
+               lease = NULL;
        }
 
-       if (a && (a->flags & OAF_BOUND) && a->fr_ip) {
-               *fr_serverid = a->fr_ip->addr.addr.in.s_addr;
-               dhcpv4_fr_stop(a);
+       if (lease && (lease->flags & OAF_BOUND) && lease->fr_ip) {
+               *fr_serverid = lease->fr_ip->addr.addr.in.s_addr;
+               dhcpv4_fr_stop(lease);
        }
 
        switch (req_msg) {
        case DHCPV4_MSG_RELEASE:
-               if (!a)
+               if (!lease)
                        return NULL;
 
                ubus_bcast_dhcp_event("dhcp.release", req_mac,
-                                     (struct in_addr *)&a->addr,
-                                     a->hostname, iface->ifname);
-               free_assignment(a);
-               a = NULL;
+                                     (struct in_addr *)&lease->addr,
+                                      lease->hostname, iface->ifname);
+                dhcpv4_free_lease(lease);
+                lease = NULL;
                break;
 
        case DHCPV4_MSG_DECLINE:
-               if (!a)
+               if (!lease)
                        return NULL;
 
-               a->flags &= ~OAF_BOUND;
+               lease->flags &= ~OAF_BOUND;
 
-               if (!(a->flags & OAF_STATIC) || a->lease_cfg->ipaddr != a->addr) {
-                       memset(a->hwaddr, 0, sizeof(a->hwaddr));
-                       a->valid_until = now + 3600; /* Block address for 1h */
-               } else {
-                       a->valid_until = now - 1;
-               }
+               if (!(lease->flags & OAF_STATIC) || lease->lease_cfg->ipaddr != lease->addr) {
+                       memset(lease->hwaddr, 0, sizeof(lease->hwaddr));
+                       lease->valid_until = now + 3600; /* Block address for 1h */
+               } else
+                       lease->valid_until = now - 1;
                break;
 
        case DHCPV4_MSG_DISCOVER:
                _fallthrough;
 
        case DHCPV4_MSG_REQUEST:
-               if (!a && iface->no_dynamic_dhcp && !lease_cfg)
+               if (!lease && iface->no_dynamic_dhcp && !lease_cfg)
                        return NULL;
 
-               /* Old assignment, but with an address that is out-of-scope? */
-               if (a && ((a->addr & iface->dhcpv4_mask.s_addr) !=
+               /* Old lease, but with an address that is out-of-scope? */
+               if (lease && ((lease->addr & iface->dhcpv4_mask.s_addr) !=
                     (iface->dhcpv4_start_ip.s_addr & iface->dhcpv4_mask.s_addr)) &&
-                   !(a->flags & OAF_STATIC)) {
+                   !(lease->flags & OAF_STATIC)) {
                        /* Try to reassign to an address that is in-scope */
-                       list_del_init(&a->head);
-                       a->addr = INADDR_ANY;
-                       if (!dhcpv4_assign(iface, a, req_addr)) {
-                               free_assignment(a);
-                               a = NULL;
+                       list_del_init(&lease->head);
+                       lease->addr = INADDR_ANY;
+                       if (!dhcpv4_assign(iface, lease, req_addr)) {
+                               dhcpv4_free_lease(lease);
+                               lease = NULL;
                                break;
                        }
                }
 
-               if (!a) {
+               if (!lease) {
                        /* Create new binding */
-                       a = alloc_assignment(0);
-                       if (!a) {
+                       lease = dhcpv4_alloc_lease(iface, req_mac, ETH_ALEN);
+                       if (!lease) {
                                warn("Failed to allocate memory for DHCPv4 lease on interface %s", iface->ifname);
                                return NULL;
                        }
 
-                       memcpy(a->hwaddr, req_mac, sizeof(a->hwaddr));
-                       a->dhcp_free_cb = dhcpv4_free_assignment;
-                       a->iface = iface;
-                       a->flags = OAF_DHCPV4;
-
                        /* static lease => infinite (0), else a placeholder */
-                       a->valid_until = lease_cfg ? 0 : now;
-                       a->addr = lease_cfg ? lease_cfg->ipaddr : INADDR_ANY;
+                       lease->valid_until = lease_cfg ? 0 : now;
+                       lease->addr = lease_cfg ? lease_cfg->ipaddr : INADDR_ANY;
 
-                       if (!dhcpv4_assign(iface, a, req_addr)) {
-                               free_assignment(a);
+                       if (!dhcpv4_assign(iface, lease, req_addr)) {
+                               dhcpv4_free_lease(lease);
                                return NULL;
                        }
 
                        if (lease_cfg) {
-                               a->flags |= OAF_STATIC;
+                               lease->flags |= OAF_STATIC;
 
                                if (lease_cfg->hostname)
-                                       a->hostname = strdup(lease_cfg->hostname);
+                                       lease->hostname = strdup(lease_cfg->hostname);
 
-                               list_add(&a->lease_cfg_list, &lease_cfg->assignments);
-                               a->lease_cfg = lease_cfg;
+                               lease_cfg->dhcpv4_lease = lease;
+                               lease->lease_cfg = lease_cfg;
                        }
                }
 
                /* See if we need to clamp the requested leasetime */
                uint32_t max_leasetime;
-               if (a->lease_cfg && a->lease_cfg->leasetime)
-                       max_leasetime = a->lease_cfg->leasetime;
+               if (lease->lease_cfg && lease->lease_cfg->leasetime)
+                       max_leasetime = lease->lease_cfg->leasetime;
                else
                        max_leasetime = iface->dhcp_leasetime;
 
@@ -565,41 +583,41 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
                        *req_leasetime = max_leasetime;
 
                if (req_msg == DHCPV4_MSG_DISCOVER) {
-                       a->flags &= ~OAF_BOUND;
+                       lease->flags &= ~OAF_BOUND;
                        *reply_incl_fr = req_accept_fr;
-                       a->valid_until = now;
+                       lease->valid_until = now;
                        break;
                }
 
-               if ((!(a->flags & OAF_STATIC) || !a->hostname) && req_hostname_len > 0) {
-                       char *new_name = realloc(a->hostname, req_hostname_len + 1);
+               if ((!(lease->flags & OAF_STATIC) || !lease->hostname) && req_hostname_len > 0) {
+                       char *new_name = realloc(lease->hostname, req_hostname_len + 1);
                        if (new_name) {
-                               a->hostname = new_name;
-                               memcpy(a->hostname, req_hostname, req_hostname_len);
-                               a->hostname[req_hostname_len] = 0;
+                               lease->hostname = new_name;
+                               memcpy(lease->hostname, req_hostname, req_hostname_len);
+                               lease->hostname[req_hostname_len] = 0;
 
-                               if (odhcpd_valid_hostname(a->hostname))
-                                       a->flags &= ~OAF_BROKEN_HOSTNAME;
+                               if (odhcpd_valid_hostname(lease->hostname))
+                                       lease->flags &= ~OAF_BROKEN_HOSTNAME;
                                else
-                                       a->flags |= OAF_BROKEN_HOSTNAME;
+                                       lease->flags |= OAF_BROKEN_HOSTNAME;
                        }
                }
 
                *reply_incl_fr = false;
-               if (!(a->flags & OAF_BOUND)) {
+               if (!(lease->flags & OAF_BOUND)) {
                        /* This is the client's first request for the address */
                        if (req_accept_fr) {
-                               a->accept_fr_nonce = true;
+                               lease->accept_fr_nonce = true;
                                *reply_incl_fr = true;
-                               odhcpd_urandom(a->key, sizeof(a->key));
+                               odhcpd_urandom(lease->key, sizeof(lease->key));
                        }
-                       a->flags |= OAF_BOUND;
+                       lease->flags |= OAF_BOUND;
                }
 
                if (*req_leasetime == UINT32_MAX)
-                       a->valid_until = 0;
+                       lease->valid_until = 0;
                else
-                       a->valid_until = (time_t)(now + *req_leasetime);
+                       lease->valid_until = (time_t)(now + *req_leasetime);
                break;
 
        default:
@@ -607,7 +625,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg req_msg, const uint8_t *re
        }
 
        dhcpv6_ia_write_statefile();
-       return a;
+       return lease;
 }
 
 static void dhcpv4_set_dest_addr(const struct interface *iface,
@@ -853,7 +871,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
        /* Misc */
        struct sockaddr_in dest_addr;
        bool reply_incl_fr = false;
-       struct dhcp_assignment *a = NULL;
+       struct dhcpv4_lease *lease = NULL;
        uint32_t fr_serverid = INADDR_ANY;
 
        if (iface->dhcpv4 == MODE_DISABLED)
@@ -944,9 +962,9 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
        case DHCPV4_MSG_DISCOVER:
                _fallthrough;
        case DHCPV4_MSG_REQUEST:
-               a = dhcpv4_lease(iface, req_msg, req->chaddr, req_addr,
-                                &req_leasetime, req_hostname, req_hostname_len,
-                                req_accept_fr, &reply_incl_fr, &fr_serverid);
+               lease = dhcpv4_lease(iface, req_msg, req->chaddr, req_addr,
+                                    &req_leasetime, req_hostname, req_hostname_len,
+                                    req_accept_fr, &reply_incl_fr, &fr_serverid);
                break;
        default:
                return;
@@ -955,19 +973,19 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
        /* We are at the point where we know the client expects a reply */
        switch (req_msg) {
        case DHCPV4_MSG_DISCOVER:
-               if (!a)
+               if (!lease)
                        return;
                reply_msg.data = DHCPV4_MSG_OFFER;
                break;
 
        case DHCPV4_MSG_REQUEST:
-               if (!a) {
+               if (!lease) {
                        reply_msg.data = DHCPV4_MSG_NAK;
                        break;
                }
 
-               if ((req_addr && req_addr != a->addr) ||
-                   (req->ciaddr.s_addr && req->ciaddr.s_addr != a->addr)) {
+               if ((req_addr && req_addr != lease->addr) ||
+                   (req->ciaddr.s_addr && req->ciaddr.s_addr != lease->addr)) {
                        reply_msg.data = DHCPV4_MSG_NAK;
                        /*
                         * DHCP client requested an IP which we can't offer to him. Probably the
@@ -1000,7 +1018,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
 
                switch (opt) {
                case DHCPV4_OPT_NETMASK:
-                       if (!a)
+                       if (!lease)
                                break;
                        reply_netmask.data = iface->dhcpv4_mask.s_addr;
                        iov[IOV_NETMASK].iov_len = sizeof(reply_netmask);
@@ -1031,11 +1049,11 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                        break;
 
                case DHCPV4_OPT_HOSTNAME:
-                       if (!a || !a->hostname)
+                       if (!lease || !lease->hostname)
                                break;
-                       reply_hostname.len = strlen(a->hostname);
+                       reply_hostname.len = strlen(lease->hostname);
                        iov[IOV_HOSTNAME].iov_len = sizeof(reply_hostname);
-                       iov[IOV_HOSTNAME_NAME].iov_base = a->hostname;
+                       iov[IOV_HOSTNAME_NAME].iov_base = lease->hostname;
                        iov[IOV_HOSTNAME_NAME].iov_len = reply_hostname.len;
                        break;
 
@@ -1053,45 +1071,45 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                        break;
 
                case DHCPV4_OPT_BROADCAST:
-                       if (!a || iface->dhcpv4_bcast.s_addr == INADDR_ANY)
+                       if (!lease || iface->dhcpv4_bcast.s_addr == INADDR_ANY)
                                break;
                        reply_broadcast.data = iface->dhcpv4_bcast.s_addr;
                        iov[IOV_BROADCAST].iov_len = sizeof(reply_broadcast);
                        break;
 
                case DHCPV4_OPT_NTPSERVER:
-                       if (!a)
+                       if (!lease)
                                break;
                        iov[IOV_NTP].iov_len = sizeof(reply_ntp);
                        iov[IOV_NTP_ADDR].iov_len = iface->dhcpv4_ntp_cnt * sizeof(*iface->dhcpv4_ntp);
                        break;
 
                case DHCPV4_OPT_LEASETIME:
-                       if (!a)
+                       if (!lease)
                                break;
                        reply_leasetime.data = htonl(req_leasetime);
                        iov[IOV_LEASETIME].iov_len = sizeof(reply_leasetime);
                        break;
 
                case DHCPV4_OPT_RENEW:
-                       if (!a || req_leasetime == UINT32_MAX)
+                       if (!lease || req_leasetime == UINT32_MAX)
                                break;
                        reply_renew.data = htonl(500 * req_leasetime / 1000);
                        iov[IOV_RENEW].iov_len = sizeof(reply_renew);
                        break;
 
                case DHCPV4_OPT_REBIND:
-                       if (!a || req_leasetime == UINT32_MAX)
+                       if (!lease || req_leasetime == UINT32_MAX)
                                break;
                        reply_rebind.data = htonl(875 * req_leasetime / 1000);
                        iov[IOV_REBIND].iov_len = sizeof(reply_rebind);
                        break;
 
                case DHCPV4_OPT_AUTHENTICATION:
-                       if (!a || !reply_incl_fr || req_msg != DHCPV4_MSG_REQUEST)
+                       if (!lease || !reply_incl_fr || req_msg != DHCPV4_MSG_REQUEST)
                                break;
 
-                       memcpy(reply_auth_body.key, a->key, sizeof(reply_auth_body.key));
+                       memcpy(reply_auth_body.key, lease->key, sizeof(reply_auth_body.key));
                        reply_auth_body.replay[0] = htonl(time(NULL));
                        reply_auth_body.replay[1] = htonl(++serial);
                        iov[IOV_AUTH].iov_len = sizeof(reply_auth);
@@ -1126,7 +1144,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                        break;
 
                case DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE:
-                       if (!a || !reply_incl_fr || req_msg == DHCPV4_MSG_REQUEST)
+                       if (!lease || !reply_incl_fr || req_msg == DHCPV4_MSG_REQUEST)
                                break;
 
                        iov[IOV_FR_NONCE_CAP].iov_len = sizeof(reply_fr_nonce_cap);
@@ -1136,7 +1154,7 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                        struct dhcpv4_dnr *dnrs;
                        size_t dnrs_len = 0;
 
-                       if (!a || reply_dnr.len > 0)
+                       if (!lease || reply_dnr.len > 0)
                                break;
 
                        for (size_t i = 0; i < iface->dnr_cnt; i++) {
@@ -1203,8 +1221,8 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                }
        }
 
-       if (a)
-               reply.yiaddr.s_addr = a->addr;
+       if (lease)
+               reply.yiaddr.s_addr = lease->addr;
 
        memcpy(reply.chaddr, req->chaddr, sizeof(reply.chaddr));
        dhcpv4_set_dest_addr(iface, reply_msg.data, req, &reply, src_addr, &dest_addr);
@@ -1223,10 +1241,10 @@ void dhcpv4_handle_msg(void *src_addr, void *data, size_t len,
                      "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
                      inet_ntoa(dest_addr.sin_addr));
 
-       if (reply_msg.data == DHCPV4_MSG_ACK && a)
+       if (reply_msg.data == DHCPV4_MSG_ACK && lease)
                ubus_bcast_dhcp_event("dhcp.ack", req->chaddr,
-                                     (struct in_addr *)&a->addr,
-                                     a->hostname, iface->ifname);
+                                     (struct in_addr *)&lease->addr,
+                                     lease->hostname, iface->ifname);
 }
 
 /* Handler for DHCPv4 messages */
@@ -1334,9 +1352,9 @@ int dhcpv4_setup_interface(struct interface *iface, bool enable)
        }
 
        if (!enable || iface->dhcpv4 == MODE_DISABLED) {
-               while (!list_empty(&iface->dhcpv4_assignments))
-                       free_assignment(list_first_entry(&iface->dhcpv4_assignments,
-                                                        struct dhcp_assignment, head));
+               while (!list_empty(&iface->dhcpv4_leases))
+                       dhcpv4_free_lease(list_first_entry(&iface->dhcpv4_leases,
+                                                          struct dhcpv4_lease, head));
                return 0;
        }
 
@@ -1420,7 +1438,7 @@ static void dhcpv4_addrlist_change(struct interface *iface)
 {
        struct odhcpd_ipaddr ip;
        struct odhcpd_ref_ip *a;
-       struct dhcp_assignment *c;
+       struct dhcpv4_lease *lease;
        uint32_t mask = iface->dhcpv4_mask.s_addr;
 
        memset(&ip, 0, sizeof(ip));
@@ -1447,12 +1465,12 @@ static void dhcpv4_addrlist_change(struct interface *iface)
                return;
        }
 
-       list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
-               if ((c->flags & OAF_BOUND) && c->fr_ip && !c->fr_cnt) {
-                       if (c->accept_fr_nonce || iface->dhcpv4_forcereconf)
-                               dhcpv4_fr_rand_delay(c);
+       list_for_each_entry(lease, &iface->dhcpv4_leases, head) {
+               if ((lease->flags & OAF_BOUND) && lease->fr_ip && !lease->fr_cnt) {
+                       if (lease->accept_fr_nonce || iface->dhcpv4_forcereconf)
+                               dhcpv4_fr_rand_delay(lease);
                        else
-                               dhcpv4_fr_stop(c);
+                               dhcpv4_fr_stop(lease);
                }
        }
 }
@@ -1482,17 +1500,17 @@ static void dhcpv4_valid_until_cb(struct uloop_timeout *event)
        time_t now = odhcpd_time();
 
        avl_for_each_element(&interfaces, iface, avl) {
-               struct dhcp_assignment *a, *n;
+               struct dhcpv4_lease *lease, *tmp;
 
                if (iface->dhcpv4 != MODE_SERVER)
                        continue;
 
-               list_for_each_entry_safe(a, n, &iface->dhcpv4_assignments, head) {
-                       if (!INFINITE_VALID(a->valid_until) && a->valid_until < now) {
-                               ubus_bcast_dhcp_event("dhcp.expire", a->hwaddr,
-                                                     (struct in_addr *)&a->addr,
-                                                     a->hostname, iface->ifname);
-                               free_assignment(a);
+               list_for_each_entry_safe(lease, tmp, &iface->dhcpv4_leases, head) {
+                       if (!INFINITE_VALID(lease->valid_until) && lease->valid_until < now) {
+                               ubus_bcast_dhcp_event("dhcp.expire", lease->hwaddr,
+                                                     (struct in_addr *)&lease->addr,
+                                                     lease->hostname, iface->ifname);
+                               dhcpv4_free_lease(lease);
                        }
                }
        }
index 73798c51a315a1c4e5faf64f7837c6906f9d3fdf..3046606898c6740a00cd0aeb8994899696718ae3 100644 (file)
@@ -462,13 +462,13 @@ static void dhcpv6_ia_write_hostsfile(time_t now)
                }
 
                if (ctxt.iface->dhcpv4 == MODE_SERVER) {
-                       struct dhcp_assignment *c;
+                       struct dhcpv4_lease *c;
 
-                       list_for_each_entry(c, &ctxt.iface->dhcpv4_assignments, head) {
+                       list_for_each_entry(c, &ctxt.iface->dhcpv4_leases, head) {
                                if (!(c->flags & OAF_BOUND))
                                        continue;
 
-                               char ipbuf[INET6_ADDRSTRLEN];
+                               char ipbuf[INET_ADDRSTRLEN];
                                struct in_addr addr = {.s_addr = c->addr};
                                inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf) - 1);
 
@@ -585,9 +585,9 @@ void dhcpv6_ia_write_statefile(void)
                        }
 
                        if (ctxt.iface->dhcpv4 == MODE_SERVER) {
-                               struct dhcp_assignment *c;
+                               struct dhcpv4_lease *c;
 
-                               list_for_each_entry(c, &ctxt.iface->dhcpv4_assignments, head) {
+                               list_for_each_entry(c, &ctxt.iface->dhcpv4_leases, head) {
                                        if (!(c->flags & OAF_BOUND))
                                                continue;
 
index 3d5d70733da6fdfd8e1b0680c0549719e0a71272..395cee530e29b7b80c4fc174fb61896769f9fd82 100644 (file)
@@ -177,9 +177,8 @@ enum odhcpd_assignment_flags {
        OAF_BOUND               = (1 << 1),
        OAF_STATIC              = (1 << 2),
        OAF_BROKEN_HOSTNAME     = (1 << 3),
-       OAF_DHCPV4              = (1 << 4),
-       OAF_DHCPV6_NA           = (1 << 5),
-       OAF_DHCPV6_PD           = (1 << 6),
+       OAF_DHCPV6_NA           = (1 << 4),
+       OAF_DHCPV6_PD           = (1 << 5),
 };
 
 /* 2-byte type + 128-byte DUID, RFC8415, §11.1 */
@@ -229,22 +228,44 @@ struct duid {
        bool iaid_set;
 };
 
+struct odhcpd_ref_ip;
+
+struct dhcpv4_lease {
+       struct list_head head;                  // struct interface->dhcpv4_assignments
+
+       struct interface *iface;                // assignment interface, non-null
+       struct lease_cfg *lease_cfg;            // host lease cfg, nullable
+
+       uint32_t addr;                          // client IP address
+       unsigned int flags;                     // OAF_*
+       time_t valid_until;                     // CLOCK_MONOTONIC time, 0 = inf
+       char *hostname;                         // client hostname
+       size_t hwaddr_len;                      // hwaddr length
+       uint8_t hwaddr[6];                      // hwaddr (only MAC supported)
+
+       // ForceRenew Nonce - RFC6704 §3.1.2
+       struct uloop_timeout fr_timer;          // FR message transmission timer
+       bool accept_fr_nonce;                   // FR client support
+       unsigned fr_cnt;                        // FR messages sent
+       uint8_t key[16];                        // FR nonce
+       struct odhcpd_ref_ip *fr_ip;            // FR message old serverid/IP
+};
+
 /* This corresponds to a UCI host section, i.e. a static lease cfg */
 struct lease_cfg {
        struct vlist_node node;
        struct list_head assignments;
+       struct dhcpv4_lease *dhcpv4_lease;
        uint32_t ipaddr;
        uint64_t hostid;
        size_t mac_count;
        struct ether_addr *macs;
        size_t duid_count;
        struct duid *duids;
-       uint32_t leasetime;
+       uint32_t leasetime;             // duration of granted leases, UINT32_MAX = inf
        char *hostname;
 };
 
-struct odhcpd_ref_ip;
-
 struct dhcp_assignment {
        struct list_head head;
        struct list_head lease_cfg_list;
@@ -258,14 +279,12 @@ struct dhcp_assignment {
        time_t valid_until;
        time_t preferred_until;
 
-       // ForceRenew - RFC6704 §3.1.2 (IPv4), RFC8415 (IPv6), §20.4, §21.11
+       // ForceRenew Nonce - RFC8415 §20.4, §21.11
        struct uloop_timeout fr_timer;
        bool accept_fr_nonce;
        int fr_cnt;
        uint8_t key[16];
-       struct odhcpd_ref_ip *fr_ip;
 
-       uint32_t addr;
        union {
                uint64_t assigned_host_id;
                uint32_t assigned_subnet_id;
@@ -281,13 +300,10 @@ struct dhcp_assignment {
        uint32_t leasetime;
        char *hostname;
 
-       uint8_t hwaddr[6];
-
        uint16_t clid_len;
        uint8_t clid_data[];
 };
 
-
 // DNR - RFC9463
 struct dnr_options {
        uint16_t priority;
@@ -348,7 +364,7 @@ struct interface {
 
        // DHCPv4 runtime data
        struct odhcpd_event dhcpv4_event;
-       struct list_head dhcpv4_assignments;
+       struct list_head dhcpv4_leases;
        struct list_head dhcpv4_fr_ips;
 
        // Managed PD
@@ -619,14 +635,20 @@ int netlink_init(void);
 int router_init(void);
 int dhcpv6_init(void);
 int ndp_init(void);
+
 #ifdef DHCPV4_SUPPORT
 int dhcpv4_init(void);
-
+void dhcpv4_free_lease(struct dhcpv4_lease *a);
 int dhcpv4_setup_interface(struct interface *iface, bool enable);
 void dhcpv4_handle_msg(void *addr, void *data, size_t len,
                       struct interface *iface, _unused void *dest_addr,
                       send_reply_cb_t send_reply, void *opaque);
-#endif
+#else
+static inline void dhcpv4_free_lease(struct dhcpv4_lease *lease) {
+       error("Trying to free IPv4 assignment 0x%p", lease);
+}
+#endif /* DHCPV4_SUPPORT */
+
 int router_setup_interface(struct interface *iface, bool enable);
 int dhcpv6_setup_interface(struct interface *iface, bool enable);
 int ndp_setup_interface(struct interface *iface, bool enable);
index 9cb94a57ff1d1c7e18b5f3ac3e8eb0438593e03a..f1ce074bbcc5cf0b2f911e50133b7e4f2774799b 100644 (file)
@@ -36,8 +36,8 @@ static int handle_dhcpv4_leases(struct ubus_context *ctx, _unused struct ubus_ob
                void *i = blobmsg_open_table(&b, iface->ifname);
                void *j = blobmsg_open_array(&b, "leases");
 
-               struct dhcp_assignment *c;
-               list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
+               struct dhcpv4_lease *c;
+               list_for_each_entry(c, &iface->dhcpv4_leases, head) {
                        if (!INFINITE_VALID(c->valid_until) && c->valid_until < now)
                                continue;